package com.enjoy.rpc.base;

import com.enjoy.rpc.StackTraceUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;

@Service
public class RpcServiceFrame {

    @Autowired
    private RegistryService registryService;


    private static class ServerTask implements Runnable {

        private final Socket socket;

        private final RegistryService registryService;

        private Logger logger = LoggerFactory.getLogger(RpcServiceFrame.class);

        public ServerTask(Socket socket, RegistryService registryService) {
            this.socket = socket;
            this.registryService = registryService;
        }

        @Override
        public void run() {
            try {
                final ObjectOutputStream objectOutputStream = new ObjectOutputStream(this.socket.getOutputStream());
                final ObjectInputStream objectInputStream = new ObjectInputStream(this.socket.getInputStream());
                final String serviceName = objectInputStream.readUTF();
                final String methodName = objectInputStream.readUTF();
                final Class<?>[] paramTypes = (Class<?>[]) objectInputStream.readObject();
                final Object[] args = (Object[]) objectInputStream.readObject();
                // 发现服务
                final Class<?> serviceClass = registryService.getService(serviceName);
                if (serviceClass == null) {
                    throw new RuntimeException();
                }
                final Method method = serviceClass.getMethod(methodName, paramTypes);
                final Object result = method.invoke(serviceClass.newInstance(), args);
                objectOutputStream.writeObject(result);
                objectOutputStream.flush();
            } catch (Exception e) {
                logger.error(StackTraceUtil.getStackTrace(e));
            } finally {
                try {
                    this.socket.close();
                } catch (IOException e) {
                    logger.error(StackTraceUtil.getStackTrace(e));
                }
            }
        }
    }

    public void startService(String serviceName, int port, Class<?> impl) throws IOException {
        ServerSocket serverSocket = new ServerSocket();
        serverSocket.bind(new InetSocketAddress(port));
        System.out.println("rpc server on " + port + " 运行");
        // 注册服务
        this.registryService.regService(serviceName, impl);
        try {
            while (true) {
                new Thread(new ServerTask(serverSocket.accept(), this.registryService)).start();
            }
        } finally {
            serverSocket.close();
        }
    }
}
